package towerz.states;

import towerz.system.BitmapFont;
import java.io.IOException;
import java.util.Vector;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
import towerz.component.panel.HutCreate;
import towerz.component.panel.Panel;
import towerz.component.TimerLabel;
import towerz.component.panel.HutPanel;
import towerz.component.panel.TowerSell;
import towerz.game.Cursor;
import towerz.game.Player;
import towerz.game.Sprite;
import towerz.game.entity.Bullet;
import towerz.game.entity.Enemy;
import towerz.game.entity.Entity;
import towerz.game.entity.hut.Hut;
import towerz.game.entity.hut.HutData;
import towerz.game.level.Level;
import towerz.game.entity.tower.Tower;
import towerz.game.entity.tower.TowerData;
import towerz.game.level.Dialog;
import towerz.system.StateManager;
import towerz.system.StringManager;
import towerz.system.Utilities;
import towerz.system.xml.XmlNode;

public class StateGame extends GameState {
	public StateGame(int level) {
		//Super
		super(GameState.GAME);
		
		//Initialize
		m_CurrentLevel	= level;
        
		try {            
            //Create Components
			m_DialogBox			= Image.createImage(Utilities.INTERFACE_FOLDER + "/dialog.png");
            m_BottomPanel       = Image.createImage(Utilities.INTERFACE_FOLDER + "/bottom.png");
            m_GoldImg           = Image.createImage(Utilities.INTERFACE_FOLDER + "/gold.png");
            m_LifeImg           = Image.createImage(Utilities.INTERFACE_FOLDER + "/defense.png");
			m_StartImage		= Image.createImage(Utilities.INTERFACE_FOLDER + "/start.png");
			m_GoalImage			= Image.createImage(Utilities.INTERFACE_FOLDER + "/goal.png");
			m_VictoryImg		= Image.createImage(Utilities.INTERFACE_FOLDER + "/victory_" + StringManager.instance().getTag() + ".png");
			m_VictoryString		= BitmapFont.getFont(BitmapFont.MAIN).renderTransparentText(StringManager.instance().getString("victory"), 0x00000000);
			m_TurboString		= BitmapFont.getFont(BitmapFont.MAIN).renderTransparentText(StringManager.instance().getString("turbo"), 0x00000000);
			m_TurboButton		= new Sprite(Utilities.INTERFACE_FOLDER + "/turbo.png", 2, 1);
			m_TurboButton.setPosition(6, StateManager.instance().getHeight() -  m_BottomPanel.getHeight() - m_TurboButton.getHeight() - 2);

			//Load tile image
			m_TileImg		= new Image[4];
			m_TileImg[0]	= Image.createImage(Utilities.TILE_IMAGE);
			for (int i = 0; i < 3; i++)
				m_TileImg[i + 1] = Image.createImage(m_TileImg[0], i * Utilities.TILE_WIDTH, 0, Utilities.TILE_WIDTH, Utilities.TILE_HEIGHT, 0);
			m_TileImg[0] = null;
		} catch (IOException ex) {}

		//Initialize level
		initLevel();

		//Create image
		Image PanelImage = null;
		try {
			PanelImage	= Image.createImage(Utilities.TOWERPANEL_IMAGE);
		} catch (IOException ex) {}
		int X = (StateManager.instance().getWidth()  - PanelImage.getWidth()); 
		int Y = (StateManager.instance().getHeight() - PanelImage.getHeight()) - m_BottomPanel.getHeight();

		//Pause/Return img
		m_ReturnImg = BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("pause"), 0x00000000);

		//Create panels
		m_HutPanel			= new HutPanel(PanelImage, X, Y);
		m_SellPanel			= new TowerSell(PanelImage, X, Y);
		m_HutCreation		= new HutCreate(PanelImage, X, Y);

		//Load BGM
		StateManager.instance().getMediaPlayer().load(Utilities.BGM_FOLDER + "/game.mid", "audio/midi", -1);
		StateManager.instance().getMediaPlayer().play();
		
		//Don't animate front state
		StateFront.Animate = false;
	}

	protected final void initLevel(){
		//Initialize
		m_Left			= false;
		m_Right			= false;
		m_Speed			= 1;
		m_Timer			= null;
		m_Score			= 0;
		m_Stronger		= 0;
		m_Multiplier	= 1;
		m_TutorialID	= 1;
		m_EnemySpawned	= 0;
        m_HasWon        = false;
		m_Spawned		= false;
		m_Survival		= false;
		m_ActivePanel	= null;
		m_SelectedHut	= null;
		m_DialogsLines	= null;
		m_ScoreString	= null;
		addScore(0);

		//INitialize vectors
		m_Huts			= new Vector();
		m_Towers		= new Vector();
		m_Bullets		= new Vector();
		m_Enemies		= new Vector();
		m_SoldHuts		= new Vector();
		m_SoldTowers	= new Vector();
		
		//If survival
		if (m_CurrentLevel < 0) m_Survival = true;

		//Create level
		m_Level		= new Level(Utilities.LEVEL_FOLDER + "/" + m_CurrentLevel + ".xml");
		m_Cursor	= new Cursor(m_Level.getMap().getWidth() / 2, m_Level.getMap().getHeight() / 2);
		m_Player	= new Player(m_Level);
		m_Tutorial	= m_Level.hasTutorial();

		//Get dialog
		m_CurrentDialog = m_Level.getDialog(m_Player.getWave());
		if (m_CurrentDialog != null) {
			m_DialogPosition	= m_CurrentDialog.getPosition();
			String[] Dialogs	= m_CurrentDialog.nextDialog();
					
			//Create image
			m_DialogsLines = new Image[Dialogs.length];
			for (int i = 0; i < m_DialogsLines.length; i++)
				m_DialogsLines[i] = BitmapFont.getFont(BitmapFont.MAIN).renderTransparentText(Dialogs[i], 0x00000000);
		}

		//Load enemy image
		m_Level.getWave(m_Player.getWave()).loadEnemyImage();

		//Create goal component if exist
		if (m_Level.getTimeLimit() > 0) m_Timer = new TimerLabel(m_Level.getTimeLimit(), StateManager.instance().getWidth() / 2, 2);

		checkCursorMovement(0);
		calculateDrawArea();
	}
	
	public void addScore(int score) {
		//Add
		m_Score += (score * m_Multiplier);
		m_ScoreString = BitmapFont.getFont(BitmapFont.TITLE).renderTransparentText(String.valueOf(m_Score), 0x00000000);
	}

	public void keyPressed(int key) {
		//Super
		super.keyPressed(key);

		//Skip if dialog or won
		if (m_CurrentDialog != null || m_HasWon) return;

		//Is right pressed?
		if (key == -7 || key == 35) m_Right = true;
		else if (key == -6)			m_Left	= true;
	}
	
	public void update(long time, int keys) {	
		//Super
		super.update(time * m_Speed, keys);

		//if in the middle of gameplay
        if(m_CurrentDialog == null && !m_HasWon) {
			//Check active panel
			if (m_ActivePanel != null) {
				//Update active panel
				m_ActivePanel.update(keys, m_Player.getGold(), time * m_Speed);

				//If hut creation
				if (m_ActivePanel == m_HutCreation) {
					//Check cost
					m_Cursor.setState(Cursor.NONE);
					if (m_HutCreation.getSelected() == null) m_Cursor.setState(Cursor.ERROR);
				}
			} else checkCursorMovement(keys);
			
			//Check back key
			if (m_Right) {
				checkRightButton();
				m_Right = false;
			}
			
			//If right is pressed
			if (m_Left) {
				//Sound
				StateManager.instance().playSFX("/select.wav");
		
				//Toggle turbo
				m_Speed = (m_Speed == 1) ? 2 : 1;
				m_TurboButton.setFrame(m_Speed - 1);
				
				//No longer
				m_Left = false;
			}
			

            //Update entities
			checkHut();
            checkTower(time * m_Speed);
            if (!m_Tutorial) checkEnemy(time * m_Speed);
			checkBullets(time * m_Speed);

			//Check victory
			checkGoal(time * m_Speed);
        }

		//Check fire key is pressed or no?
		if ((keys & GameCanvas.FIRE_PRESSED) != 0) checkFireKey();
	}

	protected void checkCursorMovement(int keys) {
		//Get cursor position
		int CursorX	= m_Cursor.getTileX();
		int CursorY	= m_Cursor.getTileY();

		//Check cursor movement
		if ((keys & GameCanvas.LEFT_PRESSED) != 0)	CursorX--;
		if ((keys & GameCanvas.RIGHT_PRESSED) != 0)	CursorX++;
		if ((keys & GameCanvas.UP_PRESSED) != 0)	CursorY--;
		if ((keys & GameCanvas.DOWN_PRESSED) != 0)	CursorY++;

		//Correct cursor position
		if (CursorX < 0)								CursorX = 0;
		if (CursorY < 0)								CursorY = 0;
		if (CursorX >= m_Level.getMap().getWidth())		CursorX = m_Level.getMap().getWidth() - 1;
		if (CursorY >= m_Level.getMap().getHeight())	CursorY = m_Level.getMap().getHeight() - 1;

		//Set cursor type
		if (!m_Level.getMap().isTileBuildable(CursorX, CursorY)) m_Cursor.setState(Cursor.ERROR);
		else {
			//Is there any selected tower
			if (m_SelectedTower != null) {
				//Check range
				if (!isTileInHutRange(m_SelectedHut, CursorX, CursorY)) m_Cursor.setState(Cursor.ERROR);
			} else {
				//Is there any tower there?
				if (getTowerAt(CursorX, CursorY) != null)	m_Cursor.setState(Cursor.VALID);
				else										m_Cursor.setState(Cursor.NONE);
			}
		}

        //Update cursor movement
		m_Cursor.setTilePosition(CursorX, CursorY);

		//Check cursor
		boolean Updated = false;
		if (CursorX - m_DrawStartX <= 0) {
			Updated	= true;
			m_DrawStartX--;
		} else if (m_DrawEndX - CursorX <= 1) {
			Updated	= true;
			m_DrawStartX++;
		}
		if (CursorY - m_DrawStartY <= 0) {
			Updated	= true;
			m_DrawStartY--;
		} else if (m_DrawEndY - CursorY <= 1) {
			Updated	= true;
			m_DrawStartY++;
		}

		//If update, calculate
		if (Updated) calculateDrawArea();
	}
	
	protected void checkRightButton() {		
		//Cancel sound
		StateManager.instance().playSFX("/cancel.wav");

		//Hide panel if visible
		if (m_ActivePanel != null) {
			m_ActivePanel.hide();
			m_ActivePanel = null;
			m_ReturnImg = BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("pause"), 0x00000000);

			//Null
			m_SelectedHut = null;
		} else if (m_SelectedTower != null) {
			//No tower
			m_SelectedTower = null;

			//Return to tower
			m_Cursor.setTilePosition(m_SelectedHut.getTileX(), m_SelectedHut.getTileY());
			checkCursorMovement(0);
			calculateDrawArea();

			//Show panel
			m_ActivePanel = m_HutPanel;

			//Get tower with that hut
			int CurrentBuilt = 0;
			for (int i = 0; i < m_Towers.size(); i++)
				if (((Tower)m_Towers.elementAt(i)).getHut() == m_SelectedHut) CurrentBuilt++;
			m_HutPanel.show(m_SelectedHut, m_Level, CurrentBuilt);
			m_ReturnImg = BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("return"), 0x00000000);
		} else {
			//Initialize vector
			Vector Parameters = null;
			if (m_Survival) {
				Parameters = new Vector();
				Parameters.addElement(new Integer(m_Score));
			}			
			
			//Go to pause
			StateManager.instance().goTo(GameState.PAUSE, Parameters, false);
		}
	}

	protected void checkFireKey() {
		//If in the middle of the game
		if (m_CurrentDialog == null && !m_HasWon) {
			//Get cursor position
			int CursorX = m_Cursor.getTileX();
			int CursorY = m_Cursor.getTileY();			

			//If no active panel
			if (m_ActivePanel == null) {
				//If buildable
				if (m_Level.getMap().isTileBuildable(CursorX, CursorY)) {
					//Is ther any building there?
					Hut HutThere		= getHutAt(CursorX, CursorY);
					Tower TowerThere	= getTowerAt(CursorX, CursorY);

					//Is going to build tower?
					if (m_SelectedTower != null) {
						//Check wether that's area okay or no
						if (isTileInHutRange(m_SelectedHut, CursorX, CursorY) && HutThere == null && TowerThere == null) {
							//Buy tower
							m_Player.addGold(-m_SelectedTower.getCost());
							m_Towers.addElement(new Tower(m_SelectedTower, m_SelectedHut, CursorX, CursorY));

							//Null-ing
							m_SelectedHut	= null;
							m_SelectedTower = null;
							m_ReturnImg		= BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("pause"), 0x00000000);
						
							//If tutorial
							if (m_Tutorial && m_TutorialID == 3) {
								//Next tut
								m_TutorialID++;
								
								//Check if it's the last tutorial
								if (m_Level.getTutorial(m_TutorialID) == null) {
									m_Tutorial = false;
									Utilities.putBool(Utilities.TUTORIAL_RMS, true);
									checkEnemy(-1);
								}
							}
						} else {
							//Tower not buildable : Error sound
							StateManager.instance().playSFX("/error.wav");
						}
					} else {
						//If no hut and no tower
						if (TowerThere == null && HutThere == null) {
							//Show hut creation
							m_ActivePanel = m_HutCreation;
							m_HutCreation.show(m_Level);
							m_ReturnImg = BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("return"), 0x00000000);
							
							//Play selection
							StateManager.instance().playSFX("/select.wav");
						} else if (TowerThere != null) {
							//Show sell panel
							m_ActivePanel = m_SellPanel;
							m_SellPanel.show(TowerThere);
							m_ReturnImg = BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("return"), 0x00000000);
							
							//Play selection
							StateManager.instance().playSFX("/select.wav");
						} else if (HutThere != null) {
							//Show hut panel
							m_ActivePanel = m_HutPanel;

							//Set hut as selected
							m_SelectedHut = HutThere;

							//Get tower with that hut
							int CurrentBuilt = 0;
							for (int i = 0; i < m_Towers.size(); i++)
								if (((Tower)m_Towers.elementAt(i)).getHut() == m_SelectedHut) CurrentBuilt++;
							m_HutPanel.show(HutThere, m_Level, CurrentBuilt);
							m_ReturnImg = BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("return"), 0x00000000);
							
							//Play selection
							StateManager.instance().playSFX("/select.wav");
						}
					}
				} else {
					//Tower not buildable : Error sound
					StateManager.instance().playSFX("/error.wav");
				}
			} else {
				//Check for active panel
				if (m_ActivePanel == m_HutCreation) {
					//Get hut
					HutData Data = m_HutCreation.getSelected();

					//Check cost
					if (Data != null && m_Player.getGold() >= Data.getCost()) {
						//Create hut
						m_Huts.addElement(new Hut(Data, CursorX, CursorY));

						//Update money
						m_Player.addGold(-Data.getCost());

						//Hide panel
						m_HutCreation.hide();
						m_ActivePanel = null;
						m_ReturnImg = BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("pause"), 0x00000000);
							
						//Play build
						StateManager.instance().playSFX("/build.wav");
						
						//If tutorial
						if (m_Tutorial && m_TutorialID == 1) {
							//Next tut
							m_TutorialID++;
								
							//Check if it's the last tutorial
							if (m_Level.getTutorial(m_TutorialID) == null) {
								m_Tutorial = false;
								Utilities.putBool(Utilities.TUTORIAL_RMS, true);
								checkEnemy(-1);
							}
						}
					} else {
						//Play error
						StateManager.instance().playSFX("/error.wav");
					}
				} else if (m_ActivePanel == m_HutPanel) {
					//Based on the action
					switch (m_HutPanel.getAction()) {
					case HutPanel.ACTION_TOWER:
						//Get tower with that hut
						int Current = 0;
						for (int i = 0; i < m_Towers.size(); i++)
							if (((Tower)m_Towers.elementAt(i)).getHut() == m_SelectedHut) Current++;

						//If under the limit
						if (Current < m_SelectedHut.getData().getLimit()) {
							//Get the selected tower
							m_SelectedTower = m_HutPanel.getSelectedTower();

							//if not null
							if (m_SelectedTower != null) {
								//Hide
								if (m_ActivePanel != null) m_ActivePanel.hide();
								m_ActivePanel = null;
								m_ReturnImg = BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("cancel"), 0x00000000);
							
								//Play selection
								StateManager.instance().playSFX("/select.wav");
						
								//If tutorial
								if (m_Tutorial && m_TutorialID == 2) {
									//Next tut
									m_TutorialID++;
								
									//Check if it's the last tutorial
									if (m_Level.getTutorial(m_TutorialID) == null) {
										m_Tutorial = false;
										Utilities.putBool(Utilities.TUTORIAL_RMS, true);
										checkEnemy(-1);
									}
								}
							} else {
								//Play error
								StateManager.instance().playSFX("/error.wav");
							}
						} else {
							//Error
							StateManager.instance().playSFX("/error.wav");
						}

						break;
					case HutPanel.ACTION_UPGRADE:
						//Get upgrade
						HutData UpgradeData = m_HutPanel.getSelectedUpgrade();

						//If not null
						if (UpgradeData != null) {
							//Upgrade
							m_Player.addGold(-UpgradeData.getCost());
							m_SelectedHut.upgrade(UpgradeData);

							//Hide
							m_ActivePanel.hide();
							m_ActivePanel = null;
							m_ReturnImg = BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("pause"), 0x00000000);

							//Not selected anymore
							m_SelectedHut = null;
							
							//Play selection
							StateManager.instance().playSFX("/build.wav");
						} else {
							//Play error
							StateManager.instance().playSFX("/error.wav");
						}

						break;
					case HutPanel.ACTION_SELL:
						//Get hut
						Hut SoldHut = m_SelectedHut;
						
						//If exist
						if (SoldHut != null) {
							//Sell
							m_SoldHuts.addElement(SoldHut);
							m_Player.addGold(SoldHut.getData().getSell());

							//For each towers
							for (int i = 0; i < m_Towers.size(); i++) {
								//If the hut
								if (((Tower)m_Towers.elementAt(i)).getHut() == SoldHut) {
									//Sold tower
									m_SoldTowers.addElement(m_Towers.elementAt(i));
									m_Player.addGold(((Tower)m_Towers.elementAt(i)).getData().getSell());
							
								}
							}
							//Play selection
							StateManager.instance().playSFX("/build.wav");
						}

						//Hide
						if (m_ActivePanel != null) m_ActivePanel.hide();
						m_ActivePanel	= null;
						m_ReturnImg		= BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("pause"), 0x00000000);

						//Not selected anymore
						m_SelectedHut = null;

						break;
					}
				} else if (m_ActivePanel == m_SellPanel) {
					//Sell the tower
					Tower Sold = getTowerAt(CursorX, CursorY);
					m_SoldTowers.addElement(Sold);

					//Get gold
					m_Player.addGold(Sold.getData().getSell());

					//Hide
					if (m_ActivePanel != null) m_ActivePanel.hide();
					m_ActivePanel	= null;
					m_ReturnImg		= BitmapFont.getFont(0).renderTransparentText(StringManager.instance().getString("pause"), 0x00000000);
							
					//Play selection
					StateManager.instance().playSFX("/build.wav");
				}
			}
		} else if (m_CurrentDialog != null) {
			//Get next dialog
			m_DialogPosition	= m_CurrentDialog.getPosition();
			String[] Dialogs	= m_CurrentDialog.nextDialog();

			//if null, no dialog as well
			if (Dialogs == null) {
				m_CurrentDialog.unloadCharacterImg();
				m_CurrentDialog = null;
				m_DialogsLines	= null;
			} else {
				//Create image
				m_DialogsLines = new Image[Dialogs.length];
				for (int i = 0; i < m_DialogsLines.length; i++)
					m_DialogsLines[i] = BitmapFont.getFont(BitmapFont.MAIN).renderTransparentText(Dialogs[i], 0x00000000);
			}
		} else if (m_HasWon) {
			//If survival mode
			if (m_Survival) {
				//Go to highscore
				Vector Parameters = new Vector();
				Parameters.addElement(new Integer(StateHighScore.SAVE_REQUEST));
				Parameters.addElement(new Integer(m_Score));

				StateManager.instance().goTo(GameState.HIGH_SCORE, Parameters, true);
			} else {
				//Otherwise next level
				m_CurrentLevel++;

				if (m_CurrentLevel <= 3l) {
					//Increment level in RMS
					if (m_CurrentLevel >= Utilities.getInt(Utilities.MAX_LEVEL_RMS, 1)) Utilities.putInt(Utilities.MAX_LEVEL_RMS, m_CurrentLevel);

					Vector param = new Vector(1);
					param.addElement(new Integer(m_CurrentLevel - 1));
					StateManager.instance().goTo(GameState.LEVELS, param, true);
				} else StateManager.instance().goTo(GameState.CREDITS, null, true);
			}
		}
	}

	protected void checkHut() {
		//Reset upgrade
		for (int i = 0; i < m_Huts.size(); i++) ((Hut)m_Huts.elementAt(i)).applyUpgrade(null);
		if (m_SelectedHut != null) m_SelectedHut.applyUpgrade(m_HutPanel.getSelectedUpgrade());

		//For each sold huts
		for (int i = 0; i < m_SoldHuts.size(); i++)
			m_Huts.removeElement(m_SoldHuts.elementAt(i));
		m_SoldHuts.removeAllElements();
	}

	protected void checkTower(long time) {
		//For each tower
		for (int i = 0; i < m_Towers.size(); i++)
			((Tower)m_Towers.elementAt(i)).update(time, m_Enemies);

		//For each sold tower
		for (int i = 0; i < m_SoldTowers.size(); i++)
			m_Towers.removeElement(m_SoldTowers.elementAt(i));
		m_SoldTowers.removeAllElements();
	}

	protected void checkEnemy(long time) {
		//For each enemy
		Vector DeletedEnemies = new Vector();
		for (int i = 0; i < m_Enemies.size(); i++) {
			//Update enemy
			((Enemy)m_Enemies.elementAt(i)).update(time);

			//if dead
			if (((Enemy)m_Enemies.elementAt(i)).getHealth() <= 0) {
				//Gain gold
				m_Player.addGold(((Enemy)m_Enemies.elementAt(i)).getGold());
				if (m_Survival) addScore(1); 

				//Delete
				DeletedEnemies.addElement(m_Enemies.elementAt(i));
			} else if (((Enemy)m_Enemies.elementAt(i)).getTileX() == m_Level.getFinishX() && ((Enemy)m_Enemies.elementAt(i)).getTileY() == m_Level.getFinishY()) {
                //Enemy pass the map, decrement the m_Life
                if(m_Player.getLife() > 0) {
					m_Player.decrementLife();
					
					//Reset multiplier
					m_Multiplier = 1;
				}

				DeletedEnemies.addElement(m_Enemies.elementAt(i));

                if(m_Player.getLife() <= 0) {
					//Init
					Vector param = new Vector();
					
					//If survival
					if (m_Survival) {
						param.addElement(new Integer(StateHighScore.SAVE_REQUEST));
						param.addElement(new Integer(m_Score));

						StateManager.instance().goTo(GameState.HIGH_SCORE, param, true);
					} else {
						//game over					
						param.addElement(new Integer(m_CurrentLevel));					
						StateManager.instance().goTo(GameState.GAME_OVER, param, true);
					}
					
                }
			}
		}

		//Removed deleted enemies
		for (int i = 0; i < DeletedEnemies.size(); i++)
			m_Enemies.removeElement(DeletedEnemies.elementAt(i));
		DeletedEnemies.removeAllElements();

		//Spawn enemy
		XmlNode[] EnemyData =  m_Level.getWave(m_Player.getWave()).spawnEnemy(time);
		if (EnemyData != null) {
			//Is spawned
			m_Spawned = true;

			//While there's still an enemy
			int i = 0;
			while (i < EnemyData.length) {
				//Create enemy
				Enemy NewEnemy = new Enemy(
						EnemyData[i],
						m_Level.getWave(m_Player.getWave()).getEnemyImages(),
						m_Level.getPathX(),
						m_Level.getPathY(),
						m_Level.getStartX(),
						m_Level.getStartY()
				);
				
				//If unlimited
				if (m_Level.getWave(m_Player.getWave()).isUnlimited()) {
					NewEnemy.strengthen(m_Stronger);
					
					//Increase stronger
					m_EnemySpawned++;
					m_Stronger = (int)(m_EnemySpawned / 10);
				}
				
				//Add to the list
				m_Enemies.addElement(NewEnemy);
				i++;
			}
		}
        
        if (m_Level.getWave(m_Player.getWave()).isWaveFinished() && m_Enemies.isEmpty()){
			//Unload image
			m_Level.getWave(m_Player.getWave()).unloadEnemyImage();
			
			//Next wave
            m_Player.nextWave();
			m_EnemySpawned = 0;

			//If survival, increase multipler
			if (m_Survival) m_Multiplier++;
            
            if (m_Player.getWave() == m_Level.getWaveLength()) {
                m_HasWon = true;
            } else {
				//Load enemy image
				m_Level.getWave(m_Player.getWave()).loadEnemyImage();

				//Get dialog
				m_CurrentDialog = m_Level.getDialog(m_Player.getWave());
				if (m_CurrentDialog != null) {
					m_DialogPosition	= m_CurrentDialog.getPosition();
					String[] Dialogs	= m_CurrentDialog.nextDialog();
					
					//Create image
					m_DialogsLines = new Image[Dialogs.length];
					for (int i = 0; i < m_DialogsLines.length; i++)
						m_DialogsLines[i] = BitmapFont.getFont(BitmapFont.MAIN).renderTransparentText(Dialogs[i], 0x00000000);
				}
			}
        }
	}

	protected void checkBullets(long time) {
		//For each tower, get new bullets
		Vector NewBullets = new Vector();
		for (int i = 0; i < m_Towers.size(); i++) {
			//Get array
			Bullet[] BulletArray = ((Tower)m_Towers.elementAt(i)).getBullets();

			//Add each bullet
			if (BulletArray != null)
				for (int j = 0; j < BulletArray.length; j++) NewBullets.addElement(BulletArray[j]);
		}

		//Insert new bullets
		for (int i = 0; i < NewBullets.size(); i++)
			m_Bullets.addElement(NewBullets.elementAt(i));
		NewBullets.removeAllElements();

		//For each bullet
		Vector InactiveBullets = new Vector();
		for (int i = 0; i < m_Bullets.size(); i++) {
			//Update
			Bullet Current = (Bullet)m_Bullets.elementAt(i);
			Current.update(time);

			//If inactive
			if (!Current.isActive()) {
				//Create damaged vectors
				Vector Damaged = new Vector();

				//If area, check other enemies
				if (Current.getData().getArea() <= 0) Damaged.addElement(Current.getTarget());
				else {
					//For each enemy
					long AreaSquared = Current.getData().getArea() * Current.getData().getArea();
					for (int j = 0; j < m_Enemies.size(); j++) {
						//Calculate distance to locked
						int X			= ((Enemy)m_Enemies.elementAt(j)).getX() - Current.getTarget().getX();
						int Y			= ((Enemy)m_Enemies.elementAt(j)).getY() - Current.getTarget().getY();
						long Distance	= (X * X) + (Y * Y);

						//Add to damaged if distance within area
						if (Distance <= AreaSquared) Damaged.addElement(m_Enemies.elementAt(j));
					}
				}

				//Damage enemies
				for (int j = 0; j < Damaged.size(); j++) Current.damage((Enemy)Damaged.elementAt(j));

				//Insert to inactive bullets
				InactiveBullets.addElement(Current);
			}
		}

		//For each dying bullet
		for (int i = 0; i < InactiveBullets.size(); i++) {
			//Remove
			m_Bullets.removeElement(InactiveBullets.elementAt(i));
		}
		InactiveBullets.removeAllElements();
	}

	protected void checkGoal(long time) {
		//Update timer if exist
		if (m_Timer != null) {
			m_Timer.update(time);

			//If limit, game over
			if (m_Timer.isLimit()) {
				Vector Parameter = new Vector();
				Parameter.addElement(new Integer(m_CurrentLevel));
				StateManager.instance().goTo(GameState.GAME_OVER, Parameter, true);
			}
		}

		//Check gold
		if (m_Level.getGoldGoal() > 0) {
			if (m_Player.getGold() >= m_Level.getGoldGoal()) {
				m_HasWon = true;
			}
		}
	}

	protected final void calculateDrawArea() {
        int height = StateManager.instance().getHeight() - m_BottomPanel.getHeight();
        
		//Get end point
		m_DrawEndX = m_DrawStartX + StateManager.instance().getColumn();
		m_DrawEndY = m_DrawStartY + StateManager.instance().getRow();

		//If map width is less than draw width
		if (m_DrawEndX - m_DrawStartX > m_Level.getMap().getWidth()) {
			m_DrawStartX	= 0;
			m_DrawEndX		= m_Level.getMap().getWidth();
		} else {
			//Correct limit
			if (m_DrawStartX < 0) {
				m_DrawStartX	= 0;
				m_DrawEndX		= m_DrawStartX + StateManager.instance().getColumn();
			} else if (m_DrawEndX >= m_Level.getMap().getWidth()) {
				m_DrawEndX		= m_Level.getMap().getWidth() ;
				m_DrawStartX	= m_DrawEndX - StateManager.instance().getColumn();
			}
		}

		//If map height is less than draw height
		if (m_DrawEndY - m_DrawStartY > m_Level.getMap().getHeight()) {
			m_DrawStartY	= 0;
			m_DrawEndY		= m_Level.getMap().getHeight();
		} else {
			//Correct limit
			if (m_DrawStartY < 0) {
				m_DrawStartY	= 0;
				m_DrawEndY		= m_DrawStartY + StateManager.instance().getRow();
			} else if (m_DrawEndY >= m_Level.getMap().getHeight()) {
				m_DrawEndY		= m_Level.getMap().getHeight();
				m_DrawStartY	= m_DrawEndY - StateManager.instance().getRow();
			}
		}
		
		//Reset
		m_DrawOffsetX	= 0;
		m_DrawOffsetY	= 0;
		int DrawWidth	= (m_DrawEndX - m_DrawStartX) * Utilities.TILE_WIDTH;
		int DrawHeight	= (m_DrawEndY - m_DrawStartY) * Utilities.TILE_HEIGHT;

		//Calculate offset is map is smaller than screen
		if (DrawWidth < StateManager.instance().getWidth())		m_DrawOffsetX = (StateManager.instance().getWidth() - DrawWidth) / 2;
		if (DrawHeight < height)	m_DrawOffsetY = (height - DrawHeight) / 2;

		//Calculate offset if map is bigger
		if (DrawWidth > StateManager.instance().getWidth())
			if (m_Cursor.getTileX() >= m_Level.getMap().getWidth() - 1) m_DrawOffsetX = StateManager.instance().getWidth() - DrawWidth;
		if (DrawHeight > height)
			if (m_Cursor.getTileY() >= m_Level.getMap().getHeight() - 1) m_DrawOffsetY = height - DrawHeight;
	}

	protected Hut getHutAt(int x, int y) {
		//Initial
		int i		= 0;
		Hut Found	= null;

		//For each hut
		while (Found == null && i < m_Huts.size()) {
			//Check position
			Found = (Hut)m_Huts.elementAt(i);
			if (Found.getTileX() != x || Found.getTileY() != y) Found = null;

			//Next
			i++;
		}

		//Return the hut
		return Found;
	}

	protected Tower getTowerAt(int x, int y) {
		//Initial
		int i			= 0;
		Tower TheTower	= null;

		//For each tower
		while (TheTower == null && i < m_Towers.size()) {
			//Check position
			TheTower = (Tower)m_Towers.elementAt(i);
			if (TheTower.getTileX() != x || TheTower.getTileY() != y) TheTower = null;

			//Next
			i++;
		}

		//Return the tower there
		return TheTower;
	}

	protected boolean isTileInHutRange(Hut hut, int x, int y) {
		//Skip if not valid
		if (hut == null) return false;

		//Check distance
		return ((x - hut.getTileX()) * (x - hut.getTileX())) + ((y - hut.getTileY()) * (y - hut.getTileY())) <= hut.getData().getRangeSquare();
	}

    private void sortEntitiesByY(Vector entityVector){
        Entity[] temp = new Entity[entityVector.size()];
        Entity tempSwap;
        int idxMin;

        //Copy all vector element to temp
        for(int i=0; i<entityVector.size(); ++i){
            temp[i] = (Entity) entityVector.elementAt(i);
        }

        //Selection Sort
        for(int i=0; i<temp.length - 1; ++i){
            idxMin = i;

            for(int j=i+1; j<temp.length; ++j){
                if( temp[j].getY() < temp[idxMin].getY() ){
                    idxMin = j;
                }
            }

            //Swap
            tempSwap		= temp[i];
            temp[i]			= temp[idxMin];
            temp[idxMin]	= tempSwap;
        }

        //Copy back to entityVector
        entityVector.removeAllElements();
        for(int i=0; i<temp.length; ++i){
            entityVector.addElement(temp[i]);
        }
    }
	
	public void draw(Graphics g) {
		//Super
		super.draw(g);
        
		//Draw map
		m_Level.getMap().draw(g, m_DrawStartX, m_DrawStartY, m_DrawEndX, m_DrawEndY, m_DrawOffsetX, m_DrawOffsetY);
		
		//Draw tile and cursor
		if (m_Tutorial && m_DialogsLines == null) {
			//Calcilate x and y
			int TileX = ((m_Level.getTutorial(m_TutorialID).getX() - m_DrawStartX) * Utilities.TILE_WIDTH)  + m_DrawOffsetX;
			int TileY = ((m_Level.getTutorial(m_TutorialID).getY() - m_DrawStartY) * Utilities.TILE_HEIGHT) + m_DrawOffsetY;
			g.drawImage(m_TileImg[Cursor.VALID], TileX, TileY, Graphics.TOP | Graphics.LEFT);
		}
		m_Cursor.draw(g, m_DrawStartX, m_DrawStartY, m_DrawEndX, m_DrawEndY, m_DrawOffsetX, m_DrawOffsetY);

		//If hut is being created, draw selection panel
		if (m_ActivePanel == m_HutCreation)
			drawHutTiles(g, new Hut(m_HutCreation.getSelected(), m_Cursor.getTileX(), m_Cursor.getTileY()));

		//If hut is selected, draw tiles
		if (m_SelectedHut != null) {
			//Is hut panel shown?
			if (m_HutPanel.isVisible()) {
				//Get selected tower
				if (m_HutPanel.getAction() == HutPanel.ACTION_TOWER && m_HutPanel.getSelectedTower() != null && (m_HutPanel.getTowerBuilt() < m_SelectedHut.getData().getLimit()))			drawHutTiles(g, m_SelectedHut);
				else if (m_HutPanel.getAction() == HutPanel.ACTION_UPGRADE && m_HutPanel.getSelectedUpgrade() != null)	drawHutTiles(g, m_SelectedHut, true);
				else if (m_HutPanel.getAction() == HutPanel.ACTION_SELL)												drawHutTiles(g, m_SelectedHut);
			} else drawHutTiles(g, m_SelectedHut);
		}

        //Draw entities
		drawEntities(g);

		//If hut is being created
		if (m_ActivePanel == m_HutCreation && m_HutCreation.getSelected() != null) {
			//Calculate position
			int CursorX = ((m_Cursor.getTileX() - m_DrawStartX) * Utilities.TILE_WIDTH)  + (Utilities.TILE_WIDTH / 2) + m_DrawOffsetX;
			int CursorY = ((m_Cursor.getTileY() - m_DrawStartY) * Utilities.TILE_HEIGHT) + Utilities.TILE_HEIGHT + m_DrawOffsetY;

			//Draw the hut
			g.drawImage(m_HutCreation.getSelected().getImage(), CursorX, CursorY, Graphics.HCENTER | Graphics.BOTTOM);
		}

		//If tower exist
		if (m_SelectedTower != null) {
			//Get cursor position
			int CursorX = m_Cursor.getTileX();
			int CursorY = m_Cursor.getTileY();

			//If valid
			if (m_Level.getMap().isTileBuildable(CursorX, CursorY) && getTowerAt(CursorX, CursorY) == null) {
				//Get hut
				Hut HutAtXY = getHutAt(CursorX, CursorY);

				//Is in range and okay hut
				if (isTileInHutRange(m_SelectedHut, CursorX, CursorY) && (HutAtXY == null || HutAtXY == m_SelectedHut)) {
					//Get position
					CursorX = ((m_Cursor.getTileX() - m_DrawStartX) * Utilities.TILE_WIDTH)  + (Utilities.TILE_WIDTH / 2)  + m_DrawOffsetX;
					CursorY = ((m_Cursor.getTileY() - m_DrawStartY) * Utilities.TILE_HEIGHT) + (Utilities.TILE_HEIGHT / 2) + m_DrawOffsetY;

					//Draw
					g.drawImage(m_SelectedTower.getRangeArea(), CursorX, CursorY, Graphics.HCENTER | Graphics.VCENTER);
					g.drawImage(m_SelectedTower.getImage(), CursorX, CursorY + (Utilities.TILE_HEIGHT / 2), Graphics.HCENTER | Graphics.BOTTOM);
				}
			}
		}

		//Draw GUI
		g.setColor(0x00FFFFFF);
		drawInterface(g);
		
		//Draw panel
		if (m_ActivePanel != null) m_ActivePanel.draw(g);

		//Draw m_ReturnImg
		if (m_ReturnImg != null && m_DialogsLines == null && !m_HasWon)
			g.drawImage(m_ReturnImg, StateManager.instance().getWidth() - 4, StateManager.instance().getHeight() - 2, Graphics.RIGHT | Graphics.BOTTOM);

		//Draw timer
		if (m_Timer != null) m_Timer.draw(g);

		//Is there any dialog?
		if (m_DialogsLines != null) {
			//Calculate position
			int DialogX = (StateManager.instance().getWidth() - m_DialogBox.getWidth()) / 2;
			int DialogY = 32 + (100 * m_DialogPosition);

			//Draw character if position = 0
			if(m_DialogPosition == 0 && m_CurrentDialog != null){
				if(m_CurrentDialog.getCharacterImg() != null)
					g.drawImage(m_CurrentDialog.getCharacterImg(), DialogX - 12, StateManager.instance().getHeight() - m_BottomPanel.getHeight(), Graphics.LEFT | Graphics.BOTTOM);
			}

			//Draw
			g.drawImage(m_DialogBox, DialogX, DialogY, Graphics.LEFT | Graphics.TOP);
			for (int i = 0; i < m_DialogsLines.length; i++) {
				g.drawImage(m_DialogsLines[i], DialogX + 8,  (i * 16) + DialogY + 6, 0);
			}
		} else if (m_Tutorial) {
			//Calculate position
			int TutorialX = (StateManager.instance().getWidth() - m_DialogBox.getWidth()) / 2 - 16;
			int TutorialY = 32;

			//Draw
			Image[] Tutorials = m_Level.getTutorial(m_TutorialID).getLines();
			for (int i = 0; i < Tutorials.length; i++)
				g.drawImage(Tutorials[i], TutorialX + 16, (i * 16) + TutorialY, Graphics.TOP | Graphics.LEFT);
		}

		//If win, draw victory image
		if (m_HasWon) {
			g.drawImage(m_VictoryImg, 0, 60, Graphics.LEFT | Graphics.TOP);
			g.drawImage(m_VictoryString, StateManager.instance().getWidth() / 2, 60 + m_VictoryImg.getHeight(), Graphics.HCENTER | Graphics.TOP);
		}
	}

	protected void drawEntities(Graphics g) {
		//Sort huts, towers, and enemies
        Vector Entities = new Vector();
		for (int i = 0; i < m_Huts.size(); i++)		Entities.addElement(m_Huts.elementAt(i));
		for (int i = 0; i < m_Towers.size(); i++)	Entities.addElement(m_Towers.elementAt(i));
        for (int i = 0; i < m_Enemies.size(); i++)	Entities.addElement(m_Enemies.elementAt(i));
        sortEntitiesByY(Entities);

        //Draw entities (enemies and towers)
        for (int i = 0; i < Entities.size(); i++)
            ((Entity)Entities.elementAt(i)).draw(g, m_DrawStartX, m_DrawStartY, m_DrawEndX, m_DrawEndY, m_DrawOffsetX, m_DrawOffsetY);

		//Draw bullets
		for (int i = 0; i < m_Bullets.size(); i++)
			((Bullet)m_Bullets.elementAt(i)).draw(g, m_DrawStartX, m_DrawStartY, m_DrawEndX, m_DrawEndY, m_DrawOffsetX, m_DrawOffsetY);
	}

	protected void drawHutTiles(Graphics g, Hut hut) {
		drawHutTiles(g, hut, false);
	}

	protected void drawHutTiles(Graphics g, Hut hut, boolean upgrade) {
		//Skip if no hut
		if (hut == null) return;

		//Get position
		int xPos = hut.getTileX();
		int yPos = hut.getTileY();

		//Set data
		HutData Data = upgrade ? hut.getUpgrade() : hut.getData();
		if (Data == null) return;

		//For each tile
		for (int x = xPos - Data.getRange(); x <= xPos + Data.getRange(); x++) {
			//Check inside drawing area
			if (x >= m_DrawStartX && x < m_DrawEndX) {
				for (int y = yPos - Data.getRange(); y <= yPos + Data.getRange(); y++) {
					//Is inside drawing area?
					if (y >= m_DrawStartY && y < m_DrawEndY) {
						//Check distance
						if (isTileInHutRange(new Hut(Data, hut.getTileX(), hut.getTileY()), x, y)) {
							//Get state
							int State = Cursor.OKAY;
							if (!m_Level.getMap().isTileBuildable(x, y)) State = Cursor.ERROR;
							else {
								Tower TowerAtXY = getTowerAt(x, y);

								//Is there any hut?
								if (getHutAt(x, y) != null)	State = Cursor.ERROR;
								else if (TowerAtXY != null) {
									//If own tower, valid
									if (TowerAtXY.getHut() == hut)	State = Cursor.VALID;
									else							State = Cursor.ERROR;
								}
							}

							//Calculate position
							int X = ((x - m_DrawStartX) * Utilities.TILE_WIDTH)  + m_DrawOffsetX;
							int Y = ((y - m_DrawStartY) * Utilities.TILE_HEIGHT) + m_DrawOffsetY;

							//Draw tile
							g.drawImage(m_TileImg[State], X, Y, Graphics.LEFT | Graphics.TOP);
						}
					}
				}
			}
		}
	}

	protected void drawInterface(Graphics g) {
        //Draw bottom panel
        g.drawImage(m_BottomPanel, 0, StateManager.instance().getHeight() - m_BottomPanel.getHeight(), 0);

		//Gold
		g.drawImage(m_GoldImg, 2, 2, Graphics.LEFT | Graphics.TOP);
		g.drawImage(m_Player.getGoldString(), m_GoldImg.getWidth() + 2, 5, Graphics.LEFT | Graphics.TOP);
		
		//Score
		if (m_Survival)	g.drawImage(m_ScoreString, StateManager.instance().getWidth() / 2, 6, Graphics.HCENTER | Graphics.TOP);
		
		//Turbo
		if (m_DialogsLines == null && !m_HasWon) {
			m_TurboButton.draw(g);
			g.drawImage(m_TurboString, 4, StateManager.instance().getHeight() - 2, Graphics.LEFT | Graphics.BOTTOM);

			//Wave
			if (!m_Survival) g.drawImage(m_Player.getWaveString(), StateManager.instance().getWidth() / 2, StateManager.instance().getHeight() - (32 - 10), Graphics.HCENTER | Graphics.TOP);
		}

		//If enemy hasn't spawned yet
		if (!m_Spawned) {
			//Draw start and goal
			g.drawImage(m_StartImage, 
					(((m_Level.getStartX() <= 0 ? 0 : m_Level.getStartX()) - m_DrawStartX) * Utilities.TILE_WIDTH)  + m_DrawOffsetX,
					(((m_Level.getStartY() <= 0 ? 0 : m_Level.getStartY()) - m_DrawStartY) * Utilities.TILE_HEIGHT) + m_DrawOffsetY,
					Graphics.LEFT | Graphics.TOP);
			
			//Calculate goal position
			int GoalX = m_Level.getPathX()[m_Level.getPathX().length - 1];
			int GoalY = m_Level.getPathY()[m_Level.getPathY().length - 1];
			if (GoalX >= m_Level.getMap().getWidth())	GoalX--;
			if (GoalY >= m_Level.getMap().getHeight())	GoalY--;
			
			//Draw goal
			g.drawImage(m_GoalImage,
				((GoalX - m_DrawStartX) * Utilities.TILE_WIDTH)  + m_DrawOffsetX,
				((GoalY - m_DrawStartY) * Utilities.TILE_HEIGHT) + m_DrawOffsetY,
				Graphics.LEFT | Graphics.TOP);
		}

		//Life
		g.drawImage(m_LifeImg, StateManager.instance().getWidth() - m_LifeImg.getWidth(), 2, Graphics.LEFT | Graphics.TOP);
		g.drawImage(m_Player.getLifeString(), StateManager.instance().getWidth() - m_LifeImg.getWidth() - 2, 5, Graphics.RIGHT | Graphics.TOP);
	}

	//Entities
	protected Level		m_Level;
	protected Cursor	m_Cursor;
	protected Vector	m_Towers;
	protected Vector	m_SoldTowers;
	protected Vector	m_SoldHuts;
	protected Vector	m_Bullets;
	protected Vector	m_Enemies;
	protected Vector	m_Huts;

	//Gameplay variable
	protected int		m_DialogPosition;
	protected Hut		m_SelectedHut;
	protected Dialog	m_CurrentDialog;
	protected TowerData	m_SelectedTower;
	
	//Data
	protected Player	m_Player;
    protected boolean	m_HasWon;
	protected boolean	m_Spawned;
	protected boolean	m_Tutorial;
	protected int		m_TutorialID;
	protected int		m_CurrentLevel;
	protected int		m_Speed;
	protected boolean	m_Right;
	protected boolean	m_Left;

	//Components
	protected TimerLabel	m_Timer;
	protected HutPanel		m_HutPanel;
	protected TowerSell		m_SellPanel;
	protected HutCreate		m_HutCreation;
	protected Panel			m_ActivePanel;
	protected Sprite		m_TurboButton;
	protected Image			m_TurboString;
	protected Image			m_StartImage;
	protected Image			m_GoalImage;

	//Images
    protected Image		m_DialogBox;
    protected Image		m_BottomPanel;
	protected Image		m_VictoryImg;
	protected Image		m_VictoryString;
    protected Image		m_ReturnImg;
    protected Image		m_GoldImg;
    protected Image		m_LifeImg;
	protected Image[]	m_TileImg;
	protected Image[]	m_DialogsLines;
	
	//Draw area
	protected int m_DrawEndX;
	protected int m_DrawEndY;
	protected int m_DrawStartX;
	protected int m_DrawStartY;
	protected int m_DrawOffsetX;
	protected int m_DrawOffsetY;
	
	//Survival
	protected int		m_Score;
	protected int		m_Multiplier;
	protected Image		m_ScoreString;
	protected boolean	m_Survival;
	protected int		m_Stronger;
	protected int		m_EnemySpawned;
}
